package com.google.wave.poppy;

import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.google.wave.api.AbstractRobotServlet;
import com.google.wave.api.Blip;
import com.google.wave.api.Event;
import com.google.wave.api.EventType;
import com.google.wave.api.Gadget;
import com.google.wave.api.RobotMessageBundle;
import com.google.wave.api.TextView;
import com.google.wave.api.Wavelet;
import com.google.wave.poppy.controller.EmailController;
import com.google.wave.poppy.mail.MTA;
import com.google.wave.poppy.mail.Message;
import com.google.wave.poppy.mail.SendMail;
import com.google.wave.poppy.mail.TestMail;
import com.google.wave.poppy.model.EmailStorage;

public class PoppyServlet extends AbstractRobotServlet {
	private static final long serialVersionUID = 7830783796661688194L;

	private static final String ADD = "/add";
	private static final String LIST = "/list";
	private static final String GADGET = "/gadget";
	private static final String TESTMAIL = "/testmail";
	private static final String UPDATE = "/update";
	private static final String REMOVE = "/remove";
	private static final String WELCOME_MESSAGE = "You have been added to a Google Wave ";
	
	// Data documents that are tied to a wavelet.
	private static final String EMAIL_ADDRESSES = "Email_Addresses";
	private static final String WAVE_CONTENT = "Wavelet_Blip_Data";
	private static final String EXTERNAL_PARTICIPANTS = "External_Participants";
	private static final String POPPYEMAILERS_GADGET_URL =
		"http://poppywave.googlecode.com/svn/trunk/poppyEmailers/poppyEmailers.xml";

	private String eventInfo = "";
	private static final String ID_SEPARATOR = "&POPPY_SEPARATOR&";
	private static final Logger LOG = Logger.getLogger(PoppyServlet.class.getName());

	@Override
	public void processEvents(RobotMessageBundle bundle) {
		
		if (null == bundle) {
			return;
		}
		
		Wavelet wavelet = bundle.getWavelet();
		
		// Reset the log.
		eventInfo = "";

		// The convenience method triggers the robot added event multiple times.
		if (bundle.wasSelfAdded()) {
			handleSelfAddedEvent(wavelet);
			return; // just return after we do the initial add.
		}

		for (Event e : bundle.getEvents()) {
			if (e.getType() == EventType.BLIP_SUBMITTED) {
				handleBlipSubmittedEvent(wavelet, e.getBlip(), bundle);
			} else if (e.getType() == EventType.FORM_BUTTON_CLICKED) {
				EmailController controller = new EmailController(wavelet);
				controller.handleButtonClick(bundle.getEvents());
			}
			
		}
		
		//wavelet.appendBlip().getDocument().append(eventInfo);
	}

	/**
	 * States "Hi, everybody when someone joins the conversation Along with the
	 * usage.
	 * 
	 * @param wavelet Wavelet to add message to.
	 */
	private void handleSelfAddedEvent(Wavelet wavelet) {
		EmailController controller = new EmailController(wavelet);
		controller.renderForm();
		
		// Initialize the data documents.
		wavelet.appendDataDocument(WAVE_CONTENT, "");
	}

	private void handleBlipSubmittedEvent(Wavelet wavelet, Blip blip, RobotMessageBundle bundle) {
		String text = blip.getDocument().getText();

		String[] lines = text.split("\n");

		// Process commands on a line by line basis.
		for (String line : lines) {
			if (line.contains(GADGET)) {
				String process = line.substring(line.indexOf(GADGET));
				String[] gadgetUrls = process.split("\\s");
				String emailData = wavelet.getDataDocument(EMAIL_ADDRESSES);
				for (int i = 1; i < gadgetUrls.length; i++) {
					Gadget gadget = new Gadget(gadgetUrls[i]);
					gadget.setField(EXTERNAL_PARTICIPANTS, emailData);
					Blip newBlip = wavelet.appendBlip();
					TextView doc = newBlip.getDocument();
					doc.append("\n");
					doc.appendElement(gadget);
				}
			}
			else if (line.contains(REMOVE)) {
				String emailData = wavelet.getDataDocument(EMAIL_ADDRESSES);
				String process = line.substring(line.indexOf(REMOVE));
				String[] removeCommands = process.split("\\s");
				for (int i = 1; i < removeCommands.length; i++) {
					if (emailData.contains(removeCommands[i])) {
						emailData = emailData.replace(removeCommands[i] + ",", "");
						blip.getDocument().append("\n" + removeCommands[i] + " removed.");
					} else {
						blip.getDocument().append("\n" + removeCommands[i] + " not found.");
					}
				}
				wavelet.setDataDocument(EMAIL_ADDRESSES, emailData);
			}
			
			// A test command.
			else if (line.contains(TESTMAIL)) {
				blip.getDocument().append(TestMail.testMail(line));
			}
			
			else if(line.contains(UPDATE)) {
				// Push back all the emails to the wavelets as blips on this command.
				MTA mailTransferAgent = MTA.get();
				Map<String,List<Message>> waveMessagesMap = mailTransferAgent.receiveMessages();
				for(String id: waveMessagesMap.keySet()){
					int endIndex = id.indexOf(ID_SEPARATOR);
					String waveId = id.substring(0, endIndex);
					String waveletId = id.substring(endIndex + ID_SEPARATOR.length(), id.length());
					Wavelet w = bundle.getWavelet(waveId, waveletId);
					List<Message> messages = waveMessagesMap.get(id);
					for (Message m : messages ) {
						Blip b = w.appendBlip();
						String emailText = m.getSender() + " says: \n"  + m.getBody();
						LOG.info(emailText);
						b.getDocument().append(emailText);
						addToWaveContent(w, emailText);
						// Send the message to other external participants as well.
						sendEmailToExternalParticipants(w, emailText, m.getSender());
					}
				}
				
				// Test with hard coded wave ids.
				/*String waveId = "wavesandbox.com!w+oUJE30Mw%A";
				String waveletId = "wavesandbox.com!conv+root";
				Wavelet w = bundle.getWavelet(waveId, waveletId);
				w.appendBlip.getDocument().append("we received the emails");*/
			}
			else {
				// This is the email loop. The default if we don't match any other commands.
				// get the current blip text.
				String emailText = blip.getCreator() + " says: \n" + blip.getDocument().getText();
				addToWaveContent(wavelet, emailText);
				sendEmailToExternalParticipants(wavelet, emailText, null);
			}
		}
	}

	/**
	 * Sends the welcome email to the user.
	 * 
	 * @param wavelet
	 * @param recipient Email address of the recipient
	 */
	private void sendWelcomeEmail(Wavelet wavelet, String recipient) {
		String welcomeEmailSubject = wavelet.getTitle().equals("") ? 
				WELCOME_MESSAGE : (WELCOME_MESSAGE + "(" + wavelet.getTitle() + ")");
		String welcomeEmailBody = "There is no blips in the wave yet!";
		String blipContent = wavelet.getDataDocument(WAVE_CONTENT);
		if (!("".equals(blipContent))){
			welcomeEmailBody = blipContent;
		}
		SendMail.sendEmail((wavelet.getWaveId() + ID_SEPARATOR + wavelet.getWaveletId()), 
				recipient, welcomeEmailSubject, welcomeEmailBody);
	}

	/**
	 * Add blip content to the wave specific storage document that we have. This is stored to
	 * send the current conversation for a new user added.
	 * 
	 * @param wavelet
	 * @param emailText
	 */
	private void addToWaveContent(Wavelet wavelet, String emailText) {
		// Keep track of all the blips.
		String currentWaveContent = wavelet.getDataDocument(WAVE_CONTENT);
		wavelet.setDataDocument(WAVE_CONTENT, currentWaveContent + emailText);
	}

	/**
	 * Sends emails to the external recipients.
	 * 
	 * @param wavelet
	 * @param emailText
	 * @param excludeRecipient
	 */
	private void sendEmailToExternalParticipants(Wavelet wavelet,
			String emailText, String excludeRecipient) {
		// Get the email addresses.
		String emailData = wavelet.getDataDocument(EMAIL_ADDRESSES);
		if (null == emailData) {
			// No external participants to send.
			return;
		}
		
		String[] emailAddresses = emailData.split(",");
		
		// Subject.
		String subject = getExternalEmailSubject(wavelet);

		// create and send the mail message to each user on the wavelet.
		for (String email: emailAddresses){
			if (email.equals(excludeRecipient)){
				continue;
			}
			SendMail.sendEmail((wavelet.getWaveId() + ID_SEPARATOR + wavelet.getWaveletId()), email, subject, emailText);
			eventInfo += "sent email to: ".concat(email);
		}
	}

	/**
	 * Constructs the email subject for external emails based on the wave title.
	 * 
	 * @param wavelet
	 * @return
	 */
	private String getExternalEmailSubject(Wavelet wavelet) {
		String subject = "Google Wave";
		if (!(wavelet.getTitle().equals(""))){
			subject = subject + " (" + wavelet.getTitle() + ")";
		}
		return subject;
	}
}
